package biz

import (
	"context"
	"strings"
	"time"

	"lianzi_gos/internal/mods/mpweixin/dal"
	"lianzi_gos/internal/mods/mpweixin/schema"
	"lianzi_gos/pkg/errors"
	"lianzi_gos/pkg/util"
)

// 业态表
type Business struct {
	Trans       *util.Trans
	BusinessDAL *dal.Business
}

// Query businesses from the data access object based on the provided parameters and options.
func (a *Business) Query(ctx context.Context, params schema.BusinessQueryParam) (*schema.BusinessQueryResult, error) {
	params.Pagination = false

	result, err := a.BusinessDAL.Query(ctx, params, schema.BusinessQueryOptions{
		QueryOptions: util.QueryOptions{
			OrderFields: []util.OrderByParam{
				{Field: "sequence", Direction: util.DESC},
				{Field: "created_at", Direction: util.DESC},
			},
		},
	})
	if err != nil {
		return nil, err
	}
	result.Data = result.Data.ToTree()
	return result, nil
}
func (a *Business) appendChildren(ctx context.Context, data schema.Businesses) (schema.Businesses, error) {
	if len(data) == 0 {
		return data, nil
	}

	existsInData := func(id string) bool {
		for _, item := range data {
			if item.ID == id {
				return true
			}
		}
		return false
	}

	for _, item := range data {
		childResult, err := a.BusinessDAL.Query(ctx, schema.BusinessQueryParam{
			ParentPathPrefix: item.ParentPath + item.ID + util.TreePathDelimiter,
		})
		if err != nil {
			return nil, err
		}
		for _, child := range childResult.Data {
			if existsInData(child.ID) {
				continue
			}
			data = append(data, child)
		}
	}

	return data, nil
}

// Get the specified business from the data access object.
func (a *Business) Get(ctx context.Context, id string) (*schema.Business, error) {
	business, err := a.BusinessDAL.Get(ctx, id)
	if err != nil {
		return nil, err
	} else if business == nil {
		return nil, errors.NotFound("", "Business not found")
	}
	return business, nil
}

// Create a new business in the data access object.
func (a *Business) Create(ctx context.Context, formItem *schema.BusinessForm) (*schema.Business, error) {
	business := &schema.Business{
		ID:        util.NewXID(),
		CreatedAt: time.Now(),
	}
	if parentID := formItem.ParentID; parentID != "" {
		parent, err := a.BusinessDAL.Get(ctx, parentID)
		if err != nil {
			return nil, err
		} else if parent == nil {
			return nil, errors.NotFound("", "Parent not found")
		}
		business.ParentPath = parent.ParentPath + parent.ID + util.TreePathDelimiter
	}

	if err := formItem.FillTo(business); err != nil {
		return nil, err
	}

	err := a.Trans.Exec(ctx, func(ctx context.Context) error {
		if err := a.BusinessDAL.Create(ctx, business); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return business, nil
}

// Update the specified business in the data access object.
func (a *Business) Update(ctx context.Context, id string, formItem *schema.BusinessForm) error {
	business, err := a.BusinessDAL.Get(ctx, id)
	if err != nil {
		return err
	} else if business == nil {
		return errors.NotFound("", "Business not found")
	}
	oldParentPath := business.ParentPath
	oldStatus := business.Status
	var childData schema.Businesses
	if business.ParentID != formItem.ParentID {
		if parentID := formItem.ParentID; parentID != "" {
			parent, err := a.BusinessDAL.Get(ctx, parentID)
			if err != nil {
				return err
			} else if parent == nil {
				return errors.NotFound("", "Parent not found")
			}
			business.ParentPath = parent.ParentPath + parent.ID + util.TreePathDelimiter
		} else {
			business.ParentPath = ""
		}

		childResult, err := a.BusinessDAL.Query(ctx, schema.BusinessQueryParam{
			ParentPathPrefix: oldParentPath + business.ID + util.TreePathDelimiter,
		}, schema.BusinessQueryOptions{
			QueryOptions: util.QueryOptions{
				SelectFields: []string{"id", "parent_path"},
			},
		})
		if err != nil {
			return err
		}
		childData = childResult.Data
	}

	if err := formItem.FillTo(business); err != nil {
		return err
	}
	business.UpdatedAt = time.Now()

	return a.Trans.Exec(ctx, func(ctx context.Context) error {
		if err := a.BusinessDAL.Update(ctx, business); err != nil {
			return err
		}
		if oldStatus != formItem.Status {
			opath := oldParentPath + business.ID + util.TreePathDelimiter
			if err := a.BusinessDAL.UpdateStatusByParentPath(ctx, opath, formItem.Status); err != nil {
				return err
			}
		}

		for _, child := range childData {
			opath := oldParentPath + business.ID + util.TreePathDelimiter
			npath := business.ParentPath + business.ID + util.TreePathDelimiter
			err := a.BusinessDAL.UpdateParentPath(ctx, child.ID, strings.Replace(child.ParentPath, opath, npath, 1))
			if err != nil {
				return err
			}
		}
		return nil
	})
}

// Delete the specified business from the data access object.
func (a *Business) Delete(ctx context.Context, id string) error {
	business, err := a.BusinessDAL.Get(ctx, id)
	if err != nil {
		return err
	} else if business == nil {
		return errors.NotFound("", "Business not found")
	}

	childResult, err := a.BusinessDAL.Query(ctx, schema.BusinessQueryParam{
		ParentPathPrefix: business.ParentPath + business.ID + util.TreePathDelimiter,
	}, schema.BusinessQueryOptions{
		QueryOptions: util.QueryOptions{
			SelectFields: []string{"id"},
		},
	})
	if err != nil {
		return err
	}

	return a.Trans.Exec(ctx, func(ctx context.Context) error {
		if err := a.BusinessDAL.Delete(ctx, id); err != nil {
			return err
		}
		for _, child := range childResult.Data {
			if err := a.BusinessDAL.Delete(ctx, child.ID); err != nil {
				return err
			}
		}
		return nil
	})
}
